﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using VkApiVer2.api.Enums;
using VkApiVer2.api.Users;
using VkApiVer2.api.Utils;
using VkApiVer2.extensions;

namespace VkApiVer2.api
{
    class ResponseApi<T> where T : class
    {
        public bool Error { get; set; }
        public T Response { get; set; }
        public ErrorResponse ErrorResponse { get; set; }
    }
    public enum CallType
    {
        HTTP, HTTPS
    }
    static class VkontakteApi
    {
        private static string _accessToken;
        public static string UserId = "2";
        private static NameValueCollection _defaultParams;
        private const string CallUriHTTPS = "https://api.vk.com/method/";
        private const string CallUriHTTP = "http://api.vk.com/method/";

        public static string AccessToken
        {
            get { return _accessToken; }
            set
            {
                _accessToken = value;
                _defaultParams = new NameValueCollection { { "access_token", _accessToken } };
            }
        }

        #region PrepareRequestString

        private static string GetRequestStringForMethod(string method, NameValueCollection parameters, CallType calltype)
        {
            string callUri;
            switch(calltype)
            {
                case CallType.HTTPS:
                    callUri = CallUriHTTPS;
                    break;
                case CallType.HTTP:
                    callUri = CallUriHTTP;
                    break;
                default:
                    callUri = CallUriHTTPS;
                    break;
            }
            var requestUri = new StringBuilder();
            if(parameters == null)
            {
                parameters = _defaultParams;
            }
            else
                parameters.Add(_defaultParams);

            string queryString = String.Concat("?", MyExtensions.ConstructQueryString(parameters));
            requestUri.Append(callUri).Append(method).Append(queryString);
            return requestUri.ToString();
        }

        #endregion

        #region additional methods
        private static string GetResponseCode(JObject result)
        {
            return (string)result["userList"];
        }

        private static JObject ReadJSONFromResponse(WebResponse result)
        {
            // Debug.WriteLine("Read JSON ...");
            using(var responseStream = result.GetResponseStream())
                if(responseStream != null)
                    using(var reader = new StreamReader(responseStream))
                    {
                        return JObject.Parse(reader.ReadToEnd());
                    }
            return null;
        }

        private static bool IsError(JObject jObject, out ErrorResponse error)
        {
            ErrorResponse errorResponse;
            if(!ErrorResponse.TryParseErrorResponse(jObject, out errorResponse))
            {
                error = null;
                return false;
            }
            error = errorResponse;
            return true;
        }
        #endregion

        #region Call Methods

        private static Task<JObject> CallMethodApi(string method, NameValueCollection parameters, CallType callType)
        {
            if(callType == CallType.HTTPS)
            {
                ServicePointManager.Expect100Continue = true;
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            }
            var request = (HttpWebRequest)WebRequest.Create(GetRequestStringForMethod(method, parameters, callType));
            Debug.WriteLine("[{0}]  {1}", method, request.RequestUri);

            request.ContentType = "application/json";
            request.Timeout = 20000;
            Task<WebResponse> task =
                Task.Factory.FromAsync(
                request.BeginGetResponse,
                asyncResult => request.EndGetResponse(asyncResult),
                null
                );
            return task.ContinueWith(t => ReadJSONFromResponse(t.Result));
        }

        #endregion

        #region API
        /// <summary>
        ///  
        /// </summary>
        /// <param name="parameters">
        /// <b>non required</b>>params uid = ID пользователя. По умолчанию ID текущего пользователя.
        /// </param> 
        /// <returns>
        ///  1 - is App , 0 - no App
        /// </returns>
        public static Task<ResponseApi<String>> IsAppUser(NameValueCollection parameters)
        {
            return CallMethodApi("isAppUser", parameters, CallType.HTTPS)
                .ContinueWith(
                r =>
                {

                    var responseApi = new ResponseApi<String>();
                    ErrorResponse error;
                    if(IsError(r.Result, out error))
                    {
                        responseApi.Error = true;
                        responseApi.ErrorResponse = error;
                        responseApi.Response = "0";
                        if(responseApi.ErrorResponse.ErrorCode.Equals("14"))
                        {
                            responseApi.Response = string.Format("error {0} - {1}", responseApi.ErrorResponse.ErrorCode,
                                                                 responseApi.ErrorResponse.ErrorMessage);
                            Debug.WriteLine("captcha nedeed");
                        }
                    }
                    else
                    {
                        responseApi.Response = GetResponseCode(r.Result);
                    }

                    return responseApi;
                }
                );
        }

        public static Task<JObject> GetUser(NameValueCollection parameters)
        {
            return CallMethodApi("users.get", parameters, CallType.HTTPS);
        }
        public static Task<ResponseApi<Users.User>> GetUserResponseApi(NameValueCollection parameters)
        {
            return CallMethodApi("users.get", parameters, CallType.HTTPS)
                  .ContinueWith(
                  r =>
                  {
                      var responseApi = new ResponseApi<Users.User>();
                      responseApi.Response = JsonConvert.DeserializeObject<Users.User>(r.Result["response"][0].ToString());
                      return responseApi;

                  });
        }

        public static List<Task<ResponseApi<Users.User>>> GetUsers(IEnumerable<string> uids, Field fields)
        {
            var tasksList =
                new List<Task<ResponseApi<Users.User>>>(10000);
            foreach(string uid in uids)
            {
                var parameters = new NameValueCollection
                                               {
                                                   {"uids",uid},
                                                   {"fields",FieldsUtils.ConvertFieldsToString(fields)}
                                               };
                var t = GetUserResponseApi(parameters);
                tasksList.Add(t);
            }
            return tasksList;
        }

        public static Task<ResponseApi<UserSearch>> SearchUsers(NameValueCollection parameters)
        {
            PrepareCountForUserSearchMethod(parameters);
            return CallMethodApi("users.search", parameters, CallType.HTTPS)
                .ContinueWith(r =>
                                  {
                                      var responseApi = new ResponseApi<UserSearch>();
                                      responseApi.Response = new UserSearch();
                                      try
                                      {
                                          JArray items = (JArray)r.Result["response"];
                                          int countResult = items.Count - 1;
                                          int countSearch = Int32.Parse(items[0].ToString());
                                          responseApi.Response.SetCount(countSearch);
                                          for(int i = 1; i <= countResult; i++)
                                          {
                                              var user = JsonConvert.DeserializeObject<Users.User>(items[i].ToString());
                                              responseApi.Response.Users.Add(user);
                                          }

                                      }
                                      catch(ArgumentOutOfRangeException e)
                                      {
                                          Console.WriteLine(e.Message);

                                      }
                                      return responseApi;
                                  });
        }

        private static void PrepareCountForUserSearchMethod(NameValueCollection parameters)
        {
            int value;
            if(parameters["count"] == null || !Int32.TryParse(parameters["count"], out value))
            {
                ReassignDefaultValueForCount(parameters);
            }
        }

        private static void ReassignDefaultValueForCount(NameValueCollection parameters)
        {
            parameters.Remove("count");
            parameters.Add("count", "20");
        }

        public static Task<JObject> GetUserBalance()
        {
            return CallMethodApi("getUserBalance", _defaultParams, CallType.HTTPS);

        }

        public static Task<JObject> GetUserSettings(NameValueCollection parameters)
        {
            return CallMethodApi("getUserSettings", parameters, CallType.HTTPS);
        }

        public static Task<ResponseApi<Likes.Likes>> GetLikesList(NameValueCollection parameters)
        {
            if(parameters["type"] == null)
                throw new Exception("parameter type is null");
            return CallMethodApi("likes.getList", parameters, CallType.HTTPS)
                .ContinueWith(r =>
    {
        var responseApi = new ResponseApi<Likes.Likes>();
        try
        {
            var likes = new Likes.Likes();
            likes = JsonConvert.DeserializeObject<Likes.Likes>(
                r.Result["response"].ToString());
            responseApi.Response = likes;
            return responseApi;
        }
        catch(Exception)
        {

            throw;
        }

    });
        }


        #endregion

    }
}
